package org.framework.lazy.cloud.network.heartbeat.client.netty.permeate.udp.handler;


import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOption;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.framework.lazy.cloud.network.heartbeat.client.netty.permeate.NettyClientPermeateServerVisitor;
import org.framework.lazy.cloud.network.heartbeat.client.netty.permeate.udp.advanced.ClientHandleUdpDistributeClientTransferServerPermeateChannelConnectionSuccessfulTypeAdvanced;
import org.framework.lazy.cloud.network.heartbeat.client.netty.permeate.udp.socket.NettyUdpClientPermeateServerVisitorTransferSocket;
import org.framework.lazy.cloud.network.heartbeat.common.constant.UdpMessageType;
import org.framework.lazy.cloud.network.heartbeat.common.advanced.payload.NettyProxyMsg;
import org.framework.lazy.cloud.network.heartbeat.common.utils.ChannelAttributeKeyUtils;

import java.util.UUID;

@Slf4j
public class NettyUdpClientPermeateServerVisitorHandler extends SimpleChannelInboundHandler<ByteBuf> {
    private final NettyClientPermeateServerVisitor nettyClientPermeateServerVisitor;
//    private final NettyChannelPool nettyChannelPool = new DefaultNettyChannelPool(10);

    public NettyUdpClientPermeateServerVisitorHandler(NettyClientPermeateServerVisitor nettyClientPermeateServerVisitor) {
        this.nettyClientPermeateServerVisitor = nettyClientPermeateServerVisitor;
    }

    /**
     * @param ctx
     * @throws Exception
     * @see NettyUdpClientPermeateServerVisitorTransferSocket
     * @see ClientHandleUdpDistributeClientTransferServerPermeateChannelConnectionSuccessfulTypeAdvanced
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 访客连接上代理服务器了
        Channel visitorChannel = ctx.channel();
        // 先不读取访客数据
        visitorChannel.config().setOption(ChannelOption.AUTO_READ, false);

        // 生成访客ID
        String visitorId = UUID.randomUUID().toString();
        Integer visitorPort = nettyClientPermeateServerVisitor.getVisitorPort();

        log.info("this channel with visitor port:{} use visitorId:{}", visitorPort, visitorId);
        ChannelAttributeKeyUtils.buildVisitorId(visitorChannel, visitorId);
        // 判断是否有可用的通道 如果没有创建新的通道
//        Channel transferChannel = nettyChannelPool.availableChannel(visitorId);
        // 创建访客连接服务端通道
        NettyUdpClientPermeateServerVisitorTransferSocket.buildTransferServer(nettyClientPermeateServerVisitor,visitorChannel);
        log.debug("客户端渗透服务端访客端口连接成功了,访客ID:{}", visitorId);
        super.channelActive(ctx);
    }

    @Override
    public void channelRead0(ChannelHandlerContext ctx, ByteBuf buf) {

        // 访客通道
        Channel visitorChannel = ctx.channel();

        String visitorId = ChannelAttributeKeyUtils.getVisitorId(visitorChannel);
        Channel nextChannel = ChannelAttributeKeyUtils.getNextChannel(visitorChannel);
        byte[] bytes = new byte[buf.readableBytes()];
        buf.readBytes(bytes);
        // 获取客户端通道，而后进行数据下发
        log.debug("【客户端渗透服务端】访客端口成功接收数据:{}", new String(bytes));

        // 使用访客的通信通道
        Integer visitorPort = nettyClientPermeateServerVisitor.getVisitorPort();
        String clientId = nettyClientPermeateServerVisitor.getNettyClientProperties().getClientId();
        NettyProxyMsg nettyProxyMsg = new NettyProxyMsg();
        nettyProxyMsg.setType(UdpMessageType.UDP_REPORT_CLIENT_PERMEATE_SERVER_TRANSFER);
        nettyProxyMsg.setVisitorId(visitorId);
        nettyProxyMsg.setClientId(clientId);
        nettyProxyMsg.setVisitorPort(visitorPort);
        nettyProxyMsg.setData(bytes);
        nextChannel.writeAndFlush(nettyProxyMsg);
        // 处理访客流量
        log.debug("【客户端渗透服务端】访客端口成功发送数据了 访客ID:{}", visitorId);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        String visitorId = ChannelAttributeKeyUtils.getVisitorId(channel);
        log.info("channel inactive:{}", visitorId);
        if (StringUtil.isNullOrEmpty(visitorId)) {
            super.channelInactive(ctx);
            return;
        }
        Channel nextChannel = ChannelAttributeKeyUtils.getNextChannel(channel);
        // 通信通道自动读写打开 ，然后关闭通信通道

        if (nextChannel != null && nextChannel.isActive()) {
            //  通知服务端 关闭访问通道、真实通道
            NettyProxyMsg myMsg = new NettyProxyMsg();
            myMsg.setType(UdpMessageType.UDP_UDP_REPORT_CLIENT_PERMEATE_SERVER_TRANSFER_CLOSE);
            myMsg.setVisitorId(visitorId);
            nextChannel.writeAndFlush(myMsg);
            //通信通道
            nextChannel.close();
            log.debug("关闭访问通道、真实通道 with visitorId:{}", visitorId);
        }else {
            log.debug("channel inactive:{}", nextChannel);
        }
        // 访客通道关闭
        channel.close();

        log.warn("【客户端渗透服务端】访客端口断开连接,访客ID:{}", visitorId);
        super.channelInactive(ctx);
    }

    @Override
    public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {

        if (ctx.channel().isWritable()) {
            log.info("Channel is writable again");
            // 恢复之前暂停的操作，如写入数据
        } else {
            log.info("Channel is not writable");
            // 暂停写入操作，等待可写状态
        }
        log.info("channelWritabilityChanged!");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("exceptionCaught");

        Channel channel = ctx.channel();
        String clientId = ChannelAttributeKeyUtils.getClientId(channel);
        String visitorId = ChannelAttributeKeyUtils.getVisitorId(channel);
        // 使用通信通道 下发关闭访客
        Channel nextChannel = ChannelAttributeKeyUtils.getNextChannel(ctx.channel());
        if (nextChannel != null) {
            // 下发关闭访客
            NettyProxyMsg closeRealClient = new NettyProxyMsg();
            closeRealClient.setType(UdpMessageType.UDP_UDP_REPORT_CLIENT_PERMEATE_SERVER_TRANSFER_CLOSE);
            closeRealClient.setClientId(clientId);
            closeRealClient.setVisitorId(visitorId);
            nextChannel.writeAndFlush(closeRealClient);
        }

        ctx.close();
    }
}